#include <iostream>
#include <math.h>
#include <vector>
#include <stdlib.h>

#include "iNodePref.h"

using namespace std;

const int SUCCESS = 0;
const int FAILURE = -1;


INodePref::INodePref(unsigned int maximalSize, unsigned int blockSize,
		unsigned int pointerSize) : SingleFilePref(maximalSize,blockSize,pointerSize)
{
	//creating the a template block with the maximum direct pointers
	_headerArrangement.push_back(_blockPtrRatio);
	_headerArrangement = getHeaderArrangement(_maximalSize, _blockSize, _blockPtrRatio, _headerArrangement);
}

double INodePref::getCurrentMaxFileSize(std::vector<int>& pointersPerLevel,unsigned int blockSize,float blockPtrRatio){
	double result = 0;
	for(unsigned int i=0;i<pointersPerLevel.size();i++){
		result += pointersPerLevel.at(i)*pow(floor(blockPtrRatio),i)*blockSize;
	}
	return result;
}

vector<int> INodePref::reduceBlocksArray(vector<int>& pointers,int maxSize){
	vector<int> result(pointers);
	if(result[0] > 0){
		result[0]--;
	}
	result.push_back(1);
	return result;
}

vector<int> INodePref::getHeaderArrangement(unsigned int maximalSize,unsigned int blockSize,float blockPtrRatio,vector<int> pointers){
	vector<int> reducedPointers = reduceBlocksArray(pointers,(int)floor(blockPtrRatio));
	double currentMaxFileSize = getCurrentMaxFileSize(reducedPointers,blockSize,blockPtrRatio);
	if(maximalSize <= currentMaxFileSize){
		return reducedPointers;
	}
	else{
		return getHeaderArrangement(maximalSize,blockSize,blockPtrRatio,reducedPointers);
	}
}

int INodePref::isMaximalSizeOk(){
	//checking whether the maximal file size can be stored under the constraints:
	vector<int> pointersPerLevel;
	for (unsigned int i = 0; i< _blockPtrRatio; i++)
	{
		pointersPerLevel.push_back(1);
	}
	double maximalFileSize = getCurrentMaxFileSize(pointersPerLevel,_blockSize, _blockPtrRatio);
	if(maximalFileSize < _maximalSize)
	{
		cout<<"Maximal file size cannot be stored."<<endl;
		return FAILURE;
	}
	else{
		return SUCCESS;
	}
}

int INodePref::areConstraintsPossilble(){
	if(_pointerSize>=_blockSize)
	{
		return FAILURE;
	}
	if(isMaximalSizeOk() == FAILURE){
		return FAILURE;
	}
	return SUCCESS;
}

int INodePref::singleFileSystemPerformance(unsigned int fileSize,outputStruct *output){
	int usedBlocks = 0;
	int totalMemoryAccesses = 0;
	int pointerLevelIndex = 0;
	double tempFileSize = fileSize;
	//starting from direct pointers level, we are using the needed direct blocks for the file
	//and back-calculating how many indirect pointers we need for them at each level
	while(tempFileSize > 0){
		//holding the current # of direct or indirect pointers
		int currentNumOfPtrs = _headerArrangement.at(pointerLevelIndex);
		double availableSpaceInLevel = currentNumOfPtrs*_blockSize*pow(_blockPtrRatio,pointerLevelIndex);
		double availableBlockInLevel = availableSpaceInLevel/_blockSize;
		int directBlocksUsed = ceil(tempFileSize/_blockSize);
		//if we need more direct blocks than what is available in this level we use those available
		if(directBlocksUsed >= availableBlockInLevel){
			directBlocksUsed = availableBlockInLevel;
		}
		tempFileSize -= directBlocksUsed*_blockSize;
		usedBlocks += directBlocksUsed;
		float temp = directBlocksUsed;
		//this loop counts how many indirect pointers are controlling the direct ones
		//we used at this level
		for(int i=0;i<pointerLevelIndex;i++){
			temp = ceil(temp/_blockPtrRatio);
			usedBlocks += temp;
		}
		//calculating how much space is accessed at this level
		int usedSpaceInLevel;
		if(tempFileSize >= 0 ? (usedSpaceInLevel = directBlocksUsed*_blockSize) : (usedSpaceInLevel = tempFileSize+directBlocksUsed*_blockSize))
			totalMemoryAccesses += usedSpaceInLevel*(pointerLevelIndex+2);
		pointerLevelIndex++;
	}
	//adding one more block for header it self
	usedBlocks++;
	output->space = usedBlocks*_blockSize;
	output->averageAccessNum = (float)totalMemoryAccesses/fileSize;
	return SUCCESS;
}

